/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package otcapp.experto;

import java.sql.Date;
import java.util.*;
import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import otcapp.dto.ExpedienteCedulaOtraDTO;
import otcapp.dto.PiezaDTO;
import otcapp.dto.ResolucionDTO;
import otcapp.dto.SeguimientoPiezaDTO;
import otcapp.entidades.*;
import otcapp.experto.excepciones.ExpertoConsultarPiezaException;
import otcapp.experto.excepciones.ExpertoInternoException;
import otcapp.experto.excepciones.ExpertoPiezaException;
import otcapp.fabricas.FabricaExpertos;
import otcapp.utiles.UtilesFechas;
import persistencia.Fachada;

/**
 *
 * @author diego
 */
public class ExpertoConsultarPieza extends Experto {

    private ExpertoConsultarInterno expertoInterno;
    private Date fechaActual;

    public ExpertoConsultarPieza() {
        expertoInterno = (ExpertoConsultarInterno) FabricaExpertos.getInstancia().getExperto(FabricaExpertos.expertos.CONSULTAR_INTERNO);
        fechaActual = Fachada.getInstancia().getFecha();
    }

    public List buscarAsuntos() {
        Criteria c = Fachada.getInstancia().crearCriterio(Asunto.class);
        c.addOrder(Order.asc("nombreAsunto"));
        return Fachada.getInstancia().buscar(Asunto.class, c);
    }

    public List buscarTipos() {
        Criteria c = Fachada.getInstancia().crearCriterio(TipoPieza.class);
        c.addOrder(Order.asc("nombreTipo"));
        return Fachada.getInstancia().buscar(TipoPieza.class, c);
    }

    public List buscarOficinas() {
        Criteria c = Fachada.getInstancia().crearCriterio(Oficina.class);
        c.addOrder(Order.asc("nombreOficina"));
        return Fachada.getInstancia().buscar(Oficina.class, c);
    }

    public List buscarUbicaciones() {
        Criteria criterioOrden = Fachada.getInstancia().crearCriterio(EstadoPieza.class);
        criterioOrden.addOrder(Order.asc("nombreUbicacion"));
        return Fachada.getInstancia().buscar(EstadoPieza.class, criterioOrden);
    }

    public List buscarInternos() {
        try {
            return expertoInterno.buscarInterno(null);
        } catch (ExpertoInternoException ex) {
            System.out.println("No hay internos cargados en la base de datos");
            return null;
        }
    }

    /**
     * Busca una pieza en la lista de piezas buscadas con anterioridad
     *
     * @param id
     * @return
     */
    protected Pieza buscarPiezaID(Long id) {
        Criteria criterioId = Fachada.getInstancia().crearCriterio(Pieza.class);
        criterioId.add(Restrictions.eq("id", id));
        Pieza piezaEncontrada = (Pieza) Fachada.getInstancia().buscar(Pieza.class, criterioId).get(0);
        return piezaEncontrada;
    }

    /**
     * Busca piezas segun criterio en el dto
     *
     * @param dto
     * @return lista dtoPiezas, las piezas quedan almacenadas en la variable
     * listadoPiezas del Experto
     * @throws ExpertoPiezaException
     */
    public List<PiezaDTO> buscarPiezas(PiezaDTO dto) throws ExpertoPiezaException {

        if (dto.getTipo() == null && dto.getInterno() == null && dto.getFechaEntrada() == null && dto.getAsuntos() == null) {
            throw new ExpertoPiezaException("No se han ingresado datos");
        }

        Criteria criterio = Fachada.getInstancia().crearCriterio(Pieza.class);


        if (dto.getInterno() != null) {
            criterio.add(Restrictions.eq("interno.id", dto.getInterno().getId()));
        }

        if (dto.getFechaEntrada() != null) {
            Date fechaHasta;
            if (dto.getFechaHasta() != null) {
                fechaHasta = dto.getFechaHasta();
            } else {
                fechaHasta = Fachada.getInstancia().getFecha();
            }
            criterio.add(Restrictions.between("fechaEntrada", dto.getFechaEntrada(), fechaHasta));

        }

        if (dto.getTipo() != null) {
            criterio.add(Restrictions.eq("tipoPieza.id", dto.getTipo().getId()));
        }

        if (dto.getAsuntos() != null) {
            criterio.createCriteria("asuntos").add(Restrictions.eq("id", dto.getAsuntos().get(0).getId()));
        }

        List<Pieza> piezasEncontradas = Fachada.getInstancia().buscar(Pieza.class, criterio);


        if (piezasEncontradas.isEmpty()) {
            throw new ExpertoPiezaException("No se han encontrado Piezas para los datos ingresados.");
        }

        List<PiezaDTO> piezasDTO = new ArrayList<>();


        return crearListadoDTOPiezas(piezasEncontradas);

    }

    public PiezaDTO buscarPiezaId(Long id) {
        Criteria CriterioId = Fachada.getInstancia().crearCriterio(Pieza.class);
        CriterioId.add(Restrictions.eq("id", id));
        return crearPiezaDTO((Pieza) Fachada.getInstancia().buscar(Pieza.class, CriterioId).get(0));
    }

    public List buscarPiezasSinFinalizar() {

        Criteria criterio = Fachada.getInstancia().crearCriterio(Pieza.class);
        criterio.add(Restrictions.isNull("fechaSalida"));

        List<Pieza> piezasEncontradas = Fachada.getInstancia().buscar(Pieza.class, criterio);

        List<PiezaDTO> piezasDTO = new ArrayList<>();


        return crearListadoDTOPiezas(piezasEncontradas);
    }

    /**
     * Crea una PiezaDTO con los datos de la pieza
     *
     * @param pieza
     * @return PiezaDTO
     */
    public PiezaDTO crearPiezaDTO(Pieza pieza) {

        PiezaDTO piezaDTO = getNewDTO(pieza);
        piezaDTO.setAsuntos(pieza.getAsuntos());
        piezaDTO.setDestinoPieza(pieza.getOficinaDestino());
        piezaDTO.setInicioPieza(pieza.getOficinaInicio());
        piezaDTO.setEmplazamientoDias(String.valueOf(pieza.getEmplazamientoDias()));
        piezaDTO.setEmplazamientoHoras(String.valueOf(pieza.getEmplazamientoHoras()));
        piezaDTO.setId(pieza.getId());
        piezaDTO.setIdentificacion(pieza.getIdentificacionPieza());
        piezaDTO.setInterno(expertoInterno.crearDTOInterno(pieza.getInterno()));
        if (pieza instanceof Expediente) {
            piezaDTO.setNroExpediente(((Expediente) pieza).getNroExpediente());
            setAsignacionProfesionalPieza((ExpedienteCedulaOtraDTO) piezaDTO, (Expediente) pieza);
        }
        if (pieza instanceof Resolucion) {
            ((ResolucionDTO) piezaDTO).setConclusiones(((Resolucion) pieza).getConclusiones());
            if(!((Resolucion)pieza).getSupervisiones().isEmpty()){
                for (Supervision supervicion : ((Resolucion)pieza).getSupervisiones()) {
                    if(supervicion.isActiva()){
                        ((ResolucionDTO)piezaDTO).setTrabajadorSocialAsignado(supervicion.getSupervisor());
                    }
                }
            }
        }
        piezaDTO.setObservaciones(pieza.getObservacion());
        piezaDTO.setSede(pieza.getSede());
        piezaDTO.setTipo(pieza.getTipoPieza());
        for (EstadoAsignado asignacionUbicacion : pieza.getHistorialSeguimiento()) {
            if (asignacionUbicacion.isUbicacionActual()) {
                piezaDTO.setUbicacion(asignacionUbicacion.getUbicacionAsignada().getNombreUbicacion());
                break;
            }
        }
        piezaDTO.setFechaEntrada(pieza.getFechaEntrada());
        piezaDTO.setFechaSalida(pieza.getFechaSalida());

        piezaDTO.setDiasOTC(calcularDiasPieza(pieza));
        piezaDTO.setFinalizada(pieza.isFinalizada());

        return piezaDTO;
    }

    private PiezaDTO getNewDTO(Pieza pieza) {
        if (pieza instanceof Resolucion) {
            return new ResolucionDTO();
        } else if (pieza instanceof Sentencia) {
            return new PiezaDTO();
        } else {
            return new ExpedienteCedulaOtraDTO();
        }
    }

    protected int calcularDiasPieza(Pieza pieza) {

        if (pieza.isFinalizada()) {
            return UtilesFechas.restarFechas(pieza.getFechaSalida(), pieza.getFechaEntrada());
        } else {
            return UtilesFechas.restarFechas(fechaActual, pieza.getFechaEntrada());
        }

    }

    public void setAsignacionProfesionalPieza(ExpedienteCedulaOtraDTO dtoPieza, Expediente pieza) {
        for (AsignacionProfesional asignacion : ((Expediente) pieza).getAsignacionProfesionales()) {
            if (asignacion.isActiva()) {
                switch (asignacion.getProfesionalAsignado().getTipoPersonal().getNombreTipo()) {
                    case "Psicólogo":
                        dtoPieza.setPsicologo(asignacion.getProfesionalAsignado());
                        break;
                    case "Trabajador Social":
                        dtoPieza.setTrabajadoraSocial(asignacion.getProfesionalAsignado());
                        break;
                }
            }
        }
    }

    public List<SeguimientoPiezaDTO> getHistorialSeguimientoPieza(Long idPieza) {
        List<SeguimientoPiezaDTO> historial = new ArrayList<>();

        Pieza pieza = buscarPiezaID(idPieza);

        List<EstadoAsignado> asignaciones = pieza.getHistorialSeguimiento();
        Collections.sort(asignaciones, new OrdenarSeguimiento());

        for (EstadoAsignado asignacionUbicacion : asignaciones) {
            SeguimientoPiezaDTO seguimientoDTO = new SeguimientoPiezaDTO();
            seguimientoDTO.setFecha(UtilesFechas.formatearADiaMesAnio(asignacionUbicacion.getFechaAsignacion()));
            seguimientoDTO.setActual(asignacionUbicacion.isUbicacionActual());
            seguimientoDTO.setLugar(asignacionUbicacion.getUbicacionAsignada().getNombreUbicacion());
            seguimientoDTO.setObservación(asignacionUbicacion.getObservaciones());
            historial.add(seguimientoDTO);
        }

        return historial;
    }

    /**
     * Retorna un lista de piezas asignas al profesional indicado en el
     * parametro
     *
     * @param profesional
     * @return
     */
    public List buscarPiezasProfesional(Personal profesional) {
        Criteria criterio = Fachada.getInstancia().crearCriterio(AsignacionProfesional.class);
        criterio.add(Restrictions.eq("profesionalAsignado", profesional));

        List<AsignacionProfesional> asignaciones = Fachada.getInstancia().buscar(AsignacionProfesional.class, criterio);
        List<Pieza> piezasAsignadas = new ArrayList<>();
        for (AsignacionProfesional asignacionProfesional : asignaciones) {
            if (asignacionProfesional.isActiva()) {
                piezasAsignadas.add(asignacionProfesional.getPieza());
            }
        }

        return crearListadoDTOPiezas(piezasAsignadas);

    }

    /**
     * Retorna un listado de piezas las cuales no han sido asignadas a ningun
     * profesional
     *
     * @return
     */
    public List<PiezaDTO> buscarPiezasSinAsignacionProfesional() {
        Criteria sinAsignar = Fachada.getInstancia().crearCriterio(Expediente.class);
        //sinAsignar.createCriteria("asignacionProfesionales").add(Restrictions.isNull("id"));
        sinAsignar.add(Restrictions.isEmpty("asignacionProfesionales"));
        List<Pieza> piezasEncontradas = Fachada.getInstancia().buscar(Expediente.class, sinAsignar);

        return crearListadoDTOPiezas(piezasEncontradas);
    }

    public enum estados {

        NO_ASIGNADA, ASIGNADAS, FINALIZADAS, NO_FINALIZADAS
    }

    public List buscarExpedientesYOtrasPiezasXEstado(estados estado) throws ExpertoConsultarPiezaException {
        Criteria criterio;
        switch (estado) {
            case NO_FINALIZADAS:
                criterio = Fachada.getInstancia().crearCriterio(Expediente.class).add(Restrictions.eq("finalizada", false));
                break;
            case FINALIZADAS:
                criterio = Fachada.getInstancia().crearCriterio(Expediente.class).add(Restrictions.eq("finalizada", true));
                break;
            case NO_ASIGNADA:
                criterio = Fachada.getInstancia().crearCriterio(Expediente.class).add(Restrictions.isEmpty("asignacionProfesionales"));
                break;
            case ASIGNADAS:
                criterio = Fachada.getInstancia().crearCriterio(Expediente.class).add(Restrictions.isNotEmpty("asignacionProfesionales"));
                break;
            default:
                criterio = null;
        }
        List<Pieza> piezasEncontradas = Fachada.getInstancia().buscar(Expediente.class, criterio);

        if (piezasEncontradas.isEmpty()) {
            throw new ExpertoConsultarPiezaException("No se han encontrado piezas con el estado: " + estado);
        }

        return crearListadoDTOPiezas(piezasEncontradas);
    }

    private List crearListadoDTOPiezas(List<Pieza> piezas) {

        List dtoPiezas = new ArrayList();

        for (Pieza pieza : piezas) {
            dtoPiezas.add(crearPiezaDTO(pieza));
        }

        return dtoPiezas;
    }
    
    public List buscarResolucionesNOSupervisadas() throws ExpertoConsultarPiezaException{
        Criteria sinSupervision = Fachada.getInstancia().crearCriterio(Resolucion.class);
        sinSupervision.add(Restrictions.isEmpty("supervisiones"));
        List<Pieza> piezasEncontradas = Fachada.getInstancia().buscar(Resolucion.class, sinSupervision);
        
        if(piezasEncontradas.isEmpty()){
            throw new ExpertoConsultarPiezaException("No se han encontrado piezas sin supervisar.");
        }
        return crearListadoDTOPiezas(piezasEncontradas);
    }
    
    
    public List buscarResolucionesXEstado(estados estado) throws ExpertoConsultarPiezaException {
        Criteria criterio;
        switch (estado) {
            case NO_FINALIZADAS:
                criterio = Fachada.getInstancia().crearCriterio(Resolucion.class).add(Restrictions.eq("finalizada", false));
                break;
            case FINALIZADAS:
                criterio = Fachada.getInstancia().crearCriterio(Resolucion.class).add(Restrictions.eq("finalizada", true));
                break;
            case NO_ASIGNADA:
                criterio = Fachada.getInstancia().crearCriterio(Resolucion.class).add(Restrictions.isEmpty("supervisiones"));
                break;
            case ASIGNADAS:
                criterio = Fachada.getInstancia().crearCriterio(Resolucion.class).add(Restrictions.isNotEmpty("supervisiones"));
                break;
            default:
                criterio = null;
        }
        List<Pieza> piezasEncontradas = Fachada.getInstancia().buscar(Resolucion.class, criterio);

        if (piezasEncontradas.isEmpty()) {
            throw new ExpertoConsultarPiezaException("No se han encontrado piezas con el estado: " + estado);
        }

        return crearListadoDTOPiezas(piezasEncontradas);
    }
    
    public List buscarSupervisionesProfesional(Personal profesional) {
        Criteria criterio = Fachada.getInstancia().crearCriterio(Supervision.class).add(Restrictions.eq("supervisor", profesional));

        List<Supervision> supervisiones = Fachada.getInstancia().buscar(Resolucion.class, criterio);
        List<Pieza> piezasSupervisadas = new ArrayList<>();
        for (Supervision supervision : supervisiones) {
            if (supervision.isActiva()) {
                piezasSupervisadas.add(supervision.getResolucionSupervisada());
            }
        }

        return crearListadoDTOPiezas(piezasSupervisadas);

    }

    class OrdenarSeguimiento implements Comparator<EstadoAsignado> {

        @Override
        public int compare(EstadoAsignado s1, EstadoAsignado s2) {
            if (s2.getFechaAsignacion().getTime() > s1.getFechaAsignacion().getTime()) {
                return 1;
            } else if (s2.getFechaAsignacion().getTime() == s1.getFechaAsignacion().getTime()) {
                return 0;
            } else {
                return -1;
            }
        }
    }
}
